package com.yeskery.nut.application;

import com.yeskery.nut.util.PemUtils;
import com.yeskery.nut.util.StringUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

/**
 * 默认基于 <b>pem</b> 格式证书的 {@link SecureServerContext} 实现，本类只能处理如下格式的pem证书：
 * <pre>
 * -----BEGIN PRIVATE KEY-----
 * ...
 * -----END PRIVATE KEY-----
 * </pre>
 *
 * 如使用nginx格式的private.key pem证书，或如下格式的pem证书：
 * <pre>
 * -----BEGIN RSA PRIVATE KEY-----
 * ...
 * -----END RSA PRIVATE KEY-----
 * </pre>
 * 会出现java.security.InvalidKeyException: IOException : algid parse error, not a sequence
 * 的报错信息，可采取如下方式对证书进行转换：
 *
 * <ol>
 *     <li>
 *         使用openssl转换：
 *             <pre>
 * openssl pkcs8 -topk8 -inform PEM -in private.key -outform pem -nocrypt -out pkcs8Private.key
 * openssl pkcs8 -in pkcs8Private.key -nocrypt -out private.pem
 *             </pre>
 *
 *         此时经过处理的private.pem本类可以处理。
 *     </li>
 *     <li>
 *         采用BouncyCastle库中的PEMReader读取PEM格式的私钥
 *         <pre>
 * PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
 * PrivateKey key = (PrivateKey)reader.readObject();
 *         </pre>
 *     </li>
 * </ol>
 *
 * 因本项目不依赖BouncyCastle，故未使用该方法实现，可使用该库进行扩展。
 * @author sprout
 * 2019-03-22 11:41
 * @version 1.0
 */
public class PemSecureServerContext implements SecureServerContext {

	/** 公钥的存储路径 */
	private final String publicPemKey;

	/** 私钥的存储路径 */
	private final String privatePemKey;

	/**
	 * 创建一个基于 <b>pem</b> 格式证书的 {@link SecureServerContext} 实现
	 * @param publicPemKey 公钥的存储路径
	 * @param privatePemKey 私钥的存储路径
	 */
	public PemSecureServerContext(String publicPemKey, String privatePemKey) {
		if (StringUtils.isEmpty(publicPemKey) || StringUtils.isEmpty(privatePemKey)) {
			throw new IllegalArgumentException("Server Key Or Private Key Must Not Be Null.");
		}
		this.publicPemKey = publicPemKey;
		this.privatePemKey = privatePemKey;
	}

	@Override
	public SSLContext getSslContext(String algorithm) throws Exception {
		KeyStore keyStore = KeyStore.getInstance("JKS");
		keyStore.load(null, null);
		//读入服务端证书
		X509Certificate certificate = PemUtils.readCertificate(publicPemKey);
		Certificate[] certificateChain = {certificate};
		//导入服务端端私钥和证书
		keyStore.setKeyEntry("serverkey", PemUtils.readPrivateKey(privatePemKey), new char[0], certificateChain);
		keyStore.setCertificateEntry("servercert", certificate);
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
		keyManagerFactory.init(keyStore, new char[0]);
		SSLContext sslContext = SSLContext.getInstance(algorithm);
		sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
		return sslContext;
	}
}
